/*
 * This file is part of algowala.
 *
 * algowala is copyright (c) 2012 Jagannathan Sampath <algowala@jagernot.com>
 *
 * algowala is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * algowala is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with algowala.  If not, see <http://www.gnu.org/licenses/>.
 *
*/
#include "tokenizer.h"
#include "command.h"
#include "chrono.h"
#include "basic_viewer.h"
#include "aw_thread.h"
#include "console.h"
#include "twap.h"
#include "tilted_twap.h"
#include "tilted_vwap.h"

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

extern cmdlist cmdlst;

#define buf cmdlst.buf
#define result cmdlst.result

extern double TIME_SCALE;
extern chrono clk;
extern basic_viewer* screen;

extern std::vector<aw_thread> threads;

extern vector<bot*> bots;

void cmdlist::add (command* cmd) {
  cmds.push_back (cmd);
  ncmds = cmds.size ();
}

void uncheck_threads (vector<aw_thread>& thrds) {
  for (int i = 0, j = thrds.size (); i < j; ++i) thrds[i].checked = 0;
}

int checked_threads (vector<aw_thread>& thrds) {
  for (int i = 0, j = thrds.size (); i < j; ++i) if (!thrds[i].checked) return 0;
  return 1;  
}

void command::execute (int operation) {
  aw_thread::opcode = operation;
    uncheck_threads (threads);
      while (!checked_threads (threads)) usleep (1); 
        aw_thread::opcode = aw_thread::tick;
}

bool set_var::operator() (tokenizer& tz) {

  string varn;
  int extract = 1;

  while (1) {
    
    if (extract) {
      tz >> varn;
      result = "";
    } else extract = 1;
    
    if (varn == "time_scale" || varn == "ts") {
      tz >> TIME_SCALE;
      clk.set_time_scale (TIME_SCALE);
    } else if (varn == "fps") {
      extern int FPS; tz >> FPS;
      if (FPS != 0) {
        extern double FPS_T; 
        FPS_T = 1. / FPS;
      }
    } else if (varn == "x") {
      tz >> screen->win_chunk.x >> screen->obj_chunk.x;
      screen->calc_visual_params ();
    } else if (varn == "y") {
      tz >> screen->win_chunk.y >> screen->obj_chunk.y;
      screen->calc_visual_params ();
    } else {
      if (varn != "") {
        result = varn + ": not found.";
        return false;
      } else break;
    }
  }

  return true;

}

bool get_var::operator() (tokenizer& tz) {
  stringstream ss;
  string varn;
  while (1) {
    tz >> varn;
    if (varn == "fps") {
      extern int FPS; ss << FPS << ' ';
    } else if (varn == "time_scale" || varn == "ts") {
      extern double TIME_SCALE; ss << TIME_SCALE << ' ';
    } else if (varn == "x") {
      ss << screen->win_chunk.x << ' ' << screen->obj_chunk.x << ' ';
    } else if (varn == "y") {
      ss << screen->win_chunk.y << ' ' << screen->obj_chunk.y << ' ';
    } else {
      if (varn != "") {
        result = varn + ": not found.";
        return false;
      } else break;
    }
  }

  result = ss.str().substr (0, ss.str().length() - 1);
  return true;

}

bool add_stock::operator() (tokenizer& tz) {

  string sym, com;
  double price, volatility;
  int volume, vol_volatility;
  int n_times, m_secs;
  
  tz >> sym >> com >> price >> volatility >> volume >> vol_volatility >> n_times >> m_secs;  
  stock _stock (sym, com, price, volatility, volume, vol_volatility, n_times, m_secs);
    
  aw_thread::add_stock (threads, _stock);  
  
  return true;

}

bool remove_stock::operator() (tokenizer& tz) {
  tz >> aw_thread::remove_sym; // remove symbol
  while (aw_thread::opcode != aw_thread::tick) usleep (1);
  execute (aw_thread::remove);
  return true;
}

bool edit_stock::operator() (tokenizer& tz) {
  
  tz >> aw_thread::edit_sym; // edit this symbol
  
  string param;
  
  aw_thread::edit_what = 0;
  while (1) {
    tz >> param;
    if (param == "symbol") {
      tz >> aw_thread::edit_stock.symbol >> aw_thread::edit_stock.company;
      aw_thread::edit_what |= aw_thread::sym;
    } else if (param == "price" || param == "p") {
      double price; tz >> price;
      aw_thread::edit_stock.price = stock_param<double> (price, aw_thread::edit_stock.price.volatility);
      aw_thread::edit_what |= aw_thread::price;
    } else if (param == "price_volatility" || param == "pv") {
      double volatility; tz >> volatility;
      aw_thread::edit_stock.price = stock_param<double> (aw_thread::edit_stock.price.value, volatility);
      aw_thread::edit_what |= aw_thread::volatility;
    } else if (param == "volume" || param == "v") {
      int volume; tz >> volume;
      aw_thread::edit_stock.volume = stock_param<int> (volume, aw_thread::edit_stock.volume.volatility);
      aw_thread::edit_what |= aw_thread::volume; 
    } else if (param == "volume_volatility" || param =="vv") {
      int vol_volatility; tz >> vol_volatility;
      aw_thread::edit_stock.volume = stock_param<int> (aw_thread::edit_stock.volume.value, vol_volatility);
      aw_thread::edit_what |= aw_thread::volume_volatility;
    } else if (param == "n_times" || param == "n") {
      tz >> aw_thread::edit_stock.n_times;
      aw_thread::edit_what |= aw_thread::n_times;
    } else if (param == "m_seconds" || param == "m") {
      tz >> aw_thread::edit_stock.m_secs;
      aw_thread::edit_what |= aw_thread::m_secs;
    } else {
      if (param != "") cons << console::red << "invalid param: " << param << eol;
      break;
    }
  }
  
  if (aw_thread::edit_what != 0) {
    while (aw_thread::opcode != aw_thread::tick) usleep(1);
    execute (aw_thread::edit);
  }
  
  return true;

}

bool get_stock::operator() (tokenizer& tz) {
  
  tz >> aw_thread::get_sym; // symbol to get

  aw_thread::get_result = "null";
  
  while (aw_thread::opcode != aw_thread::tick) usleep (1);
  execute (aw_thread::get);
  result = aw_thread::get_result;
  return true;

}

bool list_stocks::operator() (tokenizer& tz) {
  
  result = "";

  while (aw_thread::opcode != aw_thread::tick) usleep (1);
  for (int i = 0, j = threads.size (); i < j; ++i) {
    list<stock>& stocks = threads [i].stocks;
    for (stock_iterator iter = stocks.begin (), jter = stocks.end (); iter != jter; ++iter) {
      stock& si = *iter;
      result += (si.symbol + ' ');
    }
  }
  
  if (result == "") result = "null";
  return true;

}

bool add_bot::operator() (tokenizer& tz) { // adds bot into algowala
  
  string type;
  string name, symbol;
  int hs, ms, ss;
  int he, me, se;
  int exec;
  int num_shares; 
  int num_trades;
  int trade_interval_spread;
  int trade_size_spread;
  double finish_price, stop_price;
  
  tz >> type >> name >> symbol >> hs >> ms >> ss >> he >> me >> se >> exec >> num_shares >> num_trades >> finish_price >> stop_price >> trade_interval_spread >> trade_size_spread;
  
  if (type == "twap")
      bots.push_back (new twap (name, symbol, hs, ms, ss, he, me, se, exec, num_shares, num_trades, finish_price, stop_price) );
  else if (type == "tilted_twap")
      bots.push_back (new tilted_twap (name, symbol, hs, ms, ss, he, me, se, exec, num_shares, num_trades, finish_price, stop_price, trade_interval_spread, trade_size_spread) );
  else if (type == "tilted_vwap")
      bots.push_back (new tilted_vwap (name, symbol, hs, ms, ss, he, me, se, exec, num_shares, num_trades, finish_price, stop_price, trade_interval_spread, trade_size_spread) );

  return true;

}

bool select_bot::operator() (tokenizer& tz) {
  
  string name; tz >> name;
  extern vector<bot*> bots;
  extern bot* selected_bot;
  for (size_t i = 0, j = bots.size (); i < j; ++i) {
    if (bots[i]->name == name) {
      selected_bot = bots[i];
      return true;
    }
  }  
  
  cons << console::red << "bot: " << name << " not found!" << eol;
  return false;

}

stock* get_stock_ptr (const string& symbol) {
  
  while (aw_thread::opcode != aw_thread::tick) usleep (1);
  
  for (size_t i = 0, j = threads.size (); i < j; ++i) {
    aw_thread& awt = threads [i];
    for (stock_iterator p = awt.stocks.begin (), q = awt.stocks.end (); p != q; ++p) {
      stock& s = *p;
      if (s.symbol == symbol) {
        return &s;
      }
    }
  }
  
  return 0;
  
}
